package net.form.processing;

import java.awt.Color;
import java.util.ArrayList;
import java.util.List;

import net.form.Pixel;

public class CornersFinder extends AbstractImageCommand {
	

	private static int blanco = (new Color(255, 255, 255)).getRGB();

	private static int negro = (new Color(0, 0, 0)).getRGB();
	
	private Image binarizingImage;

	
	private List<Pixel> lista1, linea2, linea3, linea4;
	//Representan los pixeles del poligono cuadriculado de la muestra
	private Pixel pixel1, pixel2, pixel3, pixel4;
	
	private static final int CANTPIXELS = 100;
	
	
	public CornersFinder(Image image,
			Image binarizingImage) {
		super(image);
		this.binarizingImage = binarizingImage;
	}
	
	public Image execute() {
		if (getImage() != null) {
			List<Pixel> lista = new ArrayList<Pixel>();
			lista=findEdge(new Pixel(365, 382, new Color(255,255,255)));
			lista1=sortList(lista);
			int indexCorner= findCorner(lista1);
			pintarLinea(image,lista1);
			image.setRGB(lista1.get(indexCorner).getX(), lista1.get(indexCorner).getY(), Color.blue.getRGB());
		}
		return image;
	}
	
	private List<Pixel> sortList(List<Pixel> lista) {
		List<Pixel> result = new ArrayList<Pixel>();
		//Buscar Punto extremo
		List<Pixel> neighbords = new ArrayList<Pixel>();
		boolean ended=false;
		int index =0;
		while(!ended && index <lista.size()){
			Pixel p = lista.get(index);
			neighbords=neighbords(binarizingImage,p,result);
			int count=0;
			for (int i = 0; i < neighbords.size(); i++) {
				if(lista.contains(neighbords.get(i)))
					count++;
			}
			if(count==1){//Se encontro el pixel extremo del camino
				ended=true;
				lista.remove(p);
				while(lista.contains(p))
					lista.remove(p);
				result.add(p);
			}
			index ++;
		}
		//Buscar el resto del camito
		
//		sortListALL(result.get(0),binarizingImage,new BooleanValue(false),lista,result);
		neighbords = new ArrayList<Pixel>();
		
		index =0;
		while(lista.size()>0){
			ended=false;
			Pixel p = result.get(result.size()-1);
			neighbords=neighbords(binarizingImage,p,result);
			if(neighbords.size()== 0){
				result.remove(p);
				p = result.get(result.size()-1);
			}
			for (int i = 0; i < neighbords.size() && !ended; i++) {
				p=neighbords.get(i);
				if(lista.contains(p)){
					lista.remove(p);
					while(lista.contains(p))
						lista.remove(p);
					result.add(p);
					ended = true;
				}
			}
		}
		return result;
	}
	
	private List<Pixel> sortListALL(Pixel p, Image image, BooleanValue path,
			List<Pixel> list, List<Pixel> result) {
		List<Pixel> neighbords = neighbords(binarizingImage, p, result);
		if ( neighbords.size() == 0) {
			path.setValue(true);
		} else {
			result.add(p);
			if (list.contains(p)) {
				list.remove(p);
				while (list.contains(p))
					list.remove(p);
			}
			for (int i = 0; i < neighbords.size() && !(path.getValue()); i++){
				p=neighbords.get(i);
				if(list.contains(p)){
					result = sortListALL(neighbords.get(i), image, path, list,
							result);
				}
			}
				
				
			if (!(path.getValue())) {
				list.add(result.get(result.size() - 1));
				result.remove(result.size() - 1);
			}
		}
		return result;
	}

	private int findCorner(List<Pixel> list){
		double difMax = 0;
		double mAnt= -1;
		Pixel p1Max,p2Max;
		int indexMax=0;
	    int index = 0;
	    while(index < list.size()-5)
	    {
	    	Pixel p1=list.get(index);
	    	Pixel p2=list.get(index + 5);

	    	double x1=p1.getXCartesiano();
	    	double x2=p2.getXCartesiano();
	    	double y1=p1.getYCartesiano();
	    	double y2=p2.getYCartesiano();
	    	
	        // aproximamos tangentes con rectas de 5 pts de longitud
	    	if((x2 -x1)!=0){
		        double m = (y2 - y1) / (x2 -x1);
		        double dif =Math.abs( mAnt - m) ;
		        if ( difMax < dif) {
		        	difMax=dif;
		        	p1Max=p1;
		        	p1Max=p1;
		        	indexMax= index + 3;
		        }
		        mAnt=m;
	    	}
	        
	        index++;
	    }
	    return indexMax;
	}

	private List<Pixel> findEdge(Pixel pixel) {
		List<Pixel> list = new ArrayList<Pixel>();
		List<Pixel> visited = new ArrayList<Pixel>();
		list.add(pixel);
		List<Pixel> neighbord = neighbords(image, pixel, list);
		for (int i = 0; i < neighbord.size(); i++) {
			visited.add(neighbord.get(i));
			list.addAll(checkOff(neighbord.get(i), binarizingImage,
					new BooleanValue(false), visited, CANTPIXELS));
		}
		return list;
	}

	private List<Pixel> checkOff(Pixel pixel, Image image, BooleanValue path,
			List<Pixel> list, int cantLimit) {
		if (cantLimit == 0) {
			list.add(pixel);
			path.setValue(true);
		} else {
			list.add(pixel);
			List<Pixel> neighbord = neighbords(image, pixel, list);
			for (int i = 0; i < neighbord.size() && !(path.getValue()); i++)
				list = checkOff(neighbord.get(i), image, path, list,
						cantLimit - 1);
			if (!(path.getValue()))
				list.remove(list.size() - 1);
		}
		return list;
	}
	
	private List<Pixel> neighbords(Image image, Pixel pixel, List<Pixel> visited) {
		List<Pixel> points = new ArrayList<Pixel>();
		int x = pixel.getX();
		int y = pixel.getY();
		Pixel p=image.getPixel(x,y-1);
	    if ((image.getRGB(x,y-1) == blanco) && (!visited.contains(p)))
	        points.add(0,p);
	    p=image.getPixel(x+1,y-1);
	    if ((image.getRGB(x+1,y-1) == blanco) && (!visited.contains(p)))
	        points.add(0,p);
	    p=image.getPixel(x+1,y);
	    if ((image.getRGB(x+1,y) == blanco) && (!visited.contains(p)))
	        points.add(0,p);
	    p=image.getPixel(x+1,y+1);
	    if ((image.getRGB(x+1,y+1) == blanco) && (!visited.contains(p)))
	        points.add(0,p);
	    p=image.getPixel(x,y+1);
	    if ((image.getRGB(x,y+1) == blanco) && (!visited.contains(p)))
	        points.add(0,p);
	    p=image.getPixel(x-1,y+1);
	    if ((image.getRGB(x-1,y+1) == blanco) && (!visited.contains(p)))
	        points.add(0,p);
	    p=image.getPixel(x-1,y);
	    if ((image.getRGB(x-1,y) == blanco) && (!visited.contains(p)))
	        points.add(0,p);
	    p=image.getPixel(x-1,y-1);
	    if ((image.getRGB(x-1,y-1) == blanco) && (!visited.contains(p)))
	        points.add(0,p);
	    return points;
	}

	private Image pintarLinea(Image imageResult, List<Pixel> linea) {
		if (imageResult != null) {
			for (int i = 0; i < linea.size(); i++) {
				Pixel p1 = linea.get(i);
				imageResult.setRGB(p1.getX(), p1.getY(), Color.red.getRGB());
			}
			return imageResult;
		}
		return null;
	}
	
	@Override
	public String getCommandName() {
		return "Encontrar esquinas";
	}

}
